using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Drawing;

using KLATencor.CommonApp.Infrastructure.StateMachine;

using Syncfusion.Windows.Forms.Diagram.Controls;
using Syncfusion.Windows.Forms.Diagram;

using StateMachine_Graphical_Editor.Utils;
using StateMachine_Graphical_Editor.Shapes;
using StateMachine_Graphical_Editor.Forms;

namespace StateMachine_Graphical_Editor.Graphics_Rendering
{
    /// <summary>
    /// Implementation of initializer used by the MainForm
    /// </summary>
    public class MachineInitializer : Initializer
    {
        #region Members:

        /// <summary>
        /// The StatesFrom owned this initializer
        /// </summary>
        private StatesForm owner;

        private StateMachine root = null;
        private GState initialState;
        private GInitial initialElement;
        private PaletteGroupView palette;

        private GraphValidator graphValidator;

        /// <summary>
        /// contains all states graphical representation of this form
        /// </summary>
        private NodeCollection GStates;

        /// <summary>
        /// Contains all transitions graphical representation for this form 
        /// </summary>
        private NodeCollection GTransitions;

        /// <summary>
        /// Contains all graphical elements to be rendered for this form
        /// </summary>
        private NodeCollection GMachineElements;

        #endregion

        #region C'tor

        public MachineInitializer(PaletteGroupView palette, StateMachine machine)
        {
            this.palette = palette;
            root = machine;
            graphValidator = new GraphValidator(root.States);

            GStates = new NodeCollection();
            GTransitions = new NodeCollection();
            GMachineElements = new NodeCollection();
        }

        #endregion

        #region Initializer implementation:

        public NodeCollection getGraphicalStateList()
        {
            if (GMachineElements.Count == 0) //build for first time:
            {
                foreach (State logicState in root.States)
                {
                    GState state = new GState(palette, logicState, this.owner);
                    GStates.Add(state);
                }

                //connect transitions to first level:
                foreach (State state in root.States)
                {
                    //rendering out going transitions
                    foreach (Transition currTrans in state.Transitions)
                    {
                        if (graphValidator.unnecessaryOutGoingTransition(currTrans))
                            continue;
                        //check if multiple transitions:
                        GTransition duplicated = graphValidator.checkMultipleTransitions(currTrans, GTransitions);
                        if (duplicated != null)
                        {
                            duplicated.MultipleTransitions = true;
                            duplicated.addName(currTrans.Name);
                            continue;
                        }
                        //else:
                        Node src = findEndPointState(currTrans.Source.Name);
                        Node dst = findEndPointState(currTrans.Target.Name);

                        //TODO: now this condition not necesarry:
                        if (src != null && dst != null)
                        {
                            connectNodes(src, dst, currTrans.Name, currTrans);
                        }
                    }
                }

                //deal with in going transitions
                foreach (GState state in GStates)
                {
                    IList<Transition> inGoingTransitions = InGoingTransitionDS.getDS().getInGoingTransitions(state.LogicState);
                    if (inGoingTransitions == null || inGoingTransitions.Count == 0)
                        continue;
                    //else:
                    foreach (Transition transition in inGoingTransitions)
                    {
                        if (graphValidator.unnecessaryInGoingTransition(transition))
                            continue;
                        //check if multiple transitions:
                        GTransition duplicated = graphValidator.checkMultipleTransitions(transition, GTransitions);
                        if (duplicated != null)
                        {
                            duplicated.addName(transition.Name);
                            duplicated.MultipleTransitions = true;
                            continue;
                        }

                        //else:
                        Node source = findEndPointState(transition.Source.Name);

                        if (source != null)
                        {
                            connectNodes(source, state, transition.Name, transition);
                        }

                    }
                }

                //attach initial symbol to lowest id state:
                setInitialState();
                //deal with final states:
                setFinalState();

                GMachineElements.AddRange(GStates);
                GMachineElements.AddRange(GTransitions);
            }
            //else:
            return GMachineElements;
        }

        public StateCollection getLevelStates()
        {
            return root.States;
        }

        public Object getRoot()
        {
            return root;
        }

        public GState getInitial()
        {
            return initialState;
        }

        public void setOwner(StatesForm owner)
        {
            this.owner = owner;
        }

        public GTransition connectNodes(Node src, Node dst, String name, Transition currTrans)
        {
            //create the connector
            GTransition connector = null;
            //add Xml requierd data
            if (currTrans != null)
            {
                connector = new GTransition(PointF.Empty, new PointF(1, 1), currTrans, this.owner);
                connector.TransitionName = currTrans.Name;
                //check for cross hierardhy transition
                if (src.Name != currTrans.Source.Name ||
                    dst.Name != currTrans.Target.Name)
                    connector.CrossHierarchy = true;
            }
            else
            {
                connector = new GTransition(PointF.Empty, new PointF(1, 1), this.owner);
                connector.Id = -5;
                connector.AbbrevName = "";
                connector.SourceState = src.Name;
                connector.TargetState = dst.Name;
                connector.TransitionName = name;
                connector.EventName = "none";
            }

            //check if conncets in same hierarchy or not
            if (src is OuterState || dst is OuterState)
                connector.CrossHierarchy = true;

            //insert connector to the displayed items        
            GTransitions.Add(connector);

            src.CentralPort.TryConnect(connector.TailEndPoint);
            dst.CentralPort.TryConnect(connector.HeadEndPoint);

            return connector;
        }

        public GState getGraphicalState(State logical)
        {
            foreach (Node node in GStates)
            {
                GState tmp = node as GState;
                if (tmp != null &&
                    tmp.LogicState != null &&
                    tmp.LogicState.Equals(logical))
                {
                    return tmp;
                }
            }
            return null;
        }

        public GTransition getGraphicalTransition(Transition logical)
        {
            State source = graphValidator.getFirstAncestor(logical.Source);
            State target = graphValidator.getFirstAncestor(logical.Target);

            Node nSource = getGraphicalState(source);
            Node nTarget = getGraphicalState(target);

            foreach (GTransition gTrans in GTransitions)
            {
                if (gTrans.FromNode.Equals(nSource) && gTrans.ToNode.Equals(nTarget))
                    return gTrans;
            }
            return null;
        }

        public void switchInitial(GState newInitial)
        {
            //if not changed - do nothing
            if (newInitial.Equals(initialState) || !newInitial.IsInitialState)
                return;

            LineConnector connector = null;
            foreach (Node node in initialElement.EdgesLeaving)
            {
                connector = node as LineConnector;
            }
            if (connector != null)
            {
                GState oldInitial = connector.ToNode as GState;
                if (oldInitial != null)
                    oldInitial.IsInitialState = false;

                initialState = newInitial;

                newInitial.CentralPort.TryConnect(connector.HeadEndPoint);

                //move to proper position
                initialElement.PinPoint = new PointF(newInitial.PinPoint.X, newInitial.PinPoint.Y - 80);
            }

        }

        public GTransition checkMultipleTransitions(Transition transition)
        {
            return graphValidator.checkMultipleTransitions(transition, GTransitions);
        }

        #endregion

        #region Helper Methods:

        /// <summary>
        /// Find the initial state and connect it to the initial element 
        /// </summary>
        private void setInitialState()
        {
            foreach (Node temp in GStates)
            {
                GState state = temp as GState;
                if (state != null && state.LogicState.Equals(root.InitialState))
                {
                    {
                        //set the initial state flag
                        state.IsInitialState = true;

                        initialState = state;
                        //GInitial initial = new GInitial(palette)
                        initialElement = new GInitial(palette);
                        GMachineElements.Add(initialElement);
                        //GStates.Add(initial);
                        connectNodes(initialElement, state, "", null);
                        break;
                    }
                }
            }

        }

        /// <summary>
        /// Connect all final states to the Final element
        /// </summary>
        private void setFinalState()
        {
            GFinal centralFinal = new GFinal(palette);

            foreach (Node temp in GStates)
            {
                GState state = temp as GState;
                if (state != null)
                {
                    //final is a state with no out-going transition to other states
                    if (state.LogicState.Transitions.Count == 0)
                    {
                        state.IsFinalState = true;
                        connectNodes(state, centralFinal, "", null);
                    }
                }
            }

            if (centralFinal.EdgesEntering.Count > 0)
                GMachineElements.Add(centralFinal);
            //else:
            return;
        }

        /// <summary>
        /// Find the graphical element with the given name
        /// </summary>
        /// <param name="endPointName">The element's name</param>
        /// <returns>The element which has the name given as argument</returns>
        private Node findEndPointState(String endPointName)
        {
            Node node = GStates.FindNodeByName(endPointName);
            //if direct state not found, look for an ancestor in collection:
            if (node == null)
            {
                StateCollection subCollection = root.States;
                foreach (State state in subCollection)
                {
                    State tmp = state.Substates.Find(endPointName);
                    if (tmp != null)
                    {
                        node = GStates.FindNodeByName(state.Name);
                        break;
                    }
                }
            }           
            return node;
        }

        #endregion

    }
}
